Repository navigation
chore(ci): remove pull_request_target from privileged workflows - #349
Conversation
|
Claude finished @Piotr1215's task in 1m 16s —— View job Review: PR #349I found two blocking problems. Both are changes that break existing callers without telling them. 1. Advancing
The PR body says "advance 2. This workflow previously documented
3. Minor: the signal path can be replayed ( For fork PRs, Not verified
|
|
Claude finished @Piotr1215's task in 1m 24s —— View job Review: PR #349, re-reviewed after the latest pushThe earlier blocking issue with 1. Fork-origin replay can still trigger PAT-backed backports for any merged PR ( When the signal run comes from a fork, An outside contributor can exploit this:
GitHub only asks for approval on a contributor's first PR, so after that nothing gates this. Duplicate-branch checks and empty cherry-picks keep the damage low. Still, this lets outsiders trigger PAT mutations, so it should be closed. A cheap fix: also require the run's 2. The new guard requires
Not verified
|
|
Claude finished @Piotr1215's task in 1m 14s —— View job Review: PR #349, re-reviewed after
|
|
Claude finished @Piotr1215's task in 46s —— View job Review: PR #349, re-reviewed after
|
|
Claude finished @Piotr1215's task in 34s —— View job Review: PR #349, re-reviewed after
|
| # Only selection needs to discover PRs across the full pushed commit range. | ||
| numbers = {requested_number} if event_name == 'push' and requested_number is not None else set() | ||
| for commit in commits: | ||
| for candidate in paged(api, '/commits/%s/pulls' % commit): |
There was a problem hiding this comment.
question: If this lookup comes back empty, the push is skipped with no retry and the run passes. Is that intended? A missed lookup would mean a missed backport.
| # Review only same-repository pull requests before reading a secret or head. | ||
| if: >- | ||
| github.repository_owner == 'loft-sh' && | ||
| (github.event_name == 'pull_request' || github.event_name == format('pull_request{0}', '_target')) && |
There was a problem hiding this comment.
question: I think format() is here so the "no pull_request_target" test passes during the transition. Would it be clearer to spell it out and add an allowlist entry in the test? Then the exception is easy to find later.
| # Push restoration rechecks one selected PR and its default-branch ancestry. | ||
| # Only selection needs to discover PRs across the full pushed commit range. | ||
| numbers = {requested_number} if event_name == 'push' and requested_number is not None else set() | ||
| for commit in commits: |
There was a problem hiding this comment.
question: Rate limits look fine here. The org's limit is 15,000 an hour, and most pushes carry one commit. One edge case: in July, a vcluster-pro merge carried 4,440 commits. The 1,000-commit cap would skip a push like that with only a warning. Should that fail loudly instead?
| REPOSITORY: ${{ github.repository }} | ||
| IS_PR: ${{ github.event_name == 'pull_request_review' || github.event_name == 'pull_request_review_comment' || (github.event_name == 'issue_comment' && github.event.issue.pull_request != null) }} | ||
| PR_NUMBER: ${{ github.event.issue.pull_request && github.event.issue.number || github.event.pull_request.number }} | ||
| ASSOCIATION: ${{ github.event_name == 'issues' && github.event.issue.author_association || github.event_name == 'pull_request_review' && github.event.review.author_association || github.event.comment.author_association }} |
There was a problem hiding this comment.
nit: The issues branch never runs here. It can go.
| assert '"push","workflow_run"' in resolve | ||
| assert "github.ref == format('refs/heads/{0}', github.event.repository.default_branch)" in resolve | ||
| assert "endsWith(github.workflow_ref, format('@refs/heads/{0}', github.event.repository.default_branch))" in resolve | ||
| assert 'actions: read' in resolve |
There was a problem hiding this comment.
nit: Checks like this match strings in the YAML, so they might break on small rewording. The tests that run the authorize script and the guard expression feel stronger. Maybe worth leaning on those more?
|
Claude finished @Piotr1215's task in 41s —— View job Review: PR #349, re-reviewed after
|
Public PR automation loses its default event allowance on 2 December. Fork and conflicted PRs still need metadata events, while existing backport callers need to retain their direct event behavior. A verified relay and Node preload bridge that gap without loading contributor code.
public pr automation still required privileged trigger exceptions, and claude comment handlers could check out fork code with credentials. moving only pr metadata left that exposure and policy dependency in place. the new backport contract needs its own major tag so callers can migrate in order while existing consumers keep their current release.
github rejects a reusable workflow call before jobs start when its token permission ceiling is lower than the callee requires. the comment workflow needs contents write for same-repository edits, even though this smoke event skips its jobs. the caller must also reject forks before forwarding credentials.
A fork could replay a merged PR head and branch to trigger PAT-backed backports because signal resolution did not bind the source repository. Require the run and PR head repositories to match before admitting a merged PR. Existing review callers also skipped all reviews after the trigger migration. Keep same-repository legacy calls eligible while callers move to pull_request, and document that transition.
Labeled merges were signaled by both push and closed PR events, while each matrix restore repeated every commit lookup and could exhaust the token rate limit before backports ran. Use labeled-only signals and recheck a selected push PR with repository, PR, and ancestry requests. Keep workflow-run origin checks intact and test the request count and rejection paths. Follow-up to 2a987c3.
Stale or miswired caller outputs could backport any historical merged PR because default-branch ancestry alone did not tie restoration to the triggering push. Sync merges can introduce a side-branch merge whose history diverges from the previous default-branch head, so range membership must use newly reachable commits rather than require a linear history. Follow-up to a770a9d.
Backports can be missed while a push run reports success when commit associations are delayed or a scan exceeds its limit. The temporary review event exception also needs to be visible to maintainers.
The contract test fails when action code documents a forbidden event, blocking the rebased PR even though the workflow trigger policy still holds. Scan workflow files where the policy applies and retain the existing review workflow exception.
6932486 to
90cf9c0
Compare
|
Claude finished @Piotr1215's task in 36s —— View job Review: PR #349, re-reviewed after
|
A direct or bot commit on the default branch never gets a PR association. Since a8e3871 the resolver raised inside the commit loop, so every such push failed before writing outputs, a re-run could not recover it, and any labeled PR merged in the same push lost its backport. The resolver now collects commits that still have no PR after retries, writes outputs for the PRs it did resolve, and then warns with those commits and the remedy: re-add the backport label to start the backport through the signal. Retries run as one batch after 2 and 4 seconds, so a push with many direct commits waits 6 seconds in total rather than per commit. The pagination cap and an incomplete compare range still fail the run. Docs now match: a direct push skips with a warning, and PRs merged in the same push still run. References DEVOPS-1803
|
Claude finished @Piotr1215's task in 21s —— View job Review: PR #349, re-reviewed after
|
|
Addressed in f5d40f5: a pushed commit with no PR no longer fails the run or drops the other PRs in that push.
@vcauesantos this changes the answer to your question on |
Summary
Replace
pull_request_targettriggers under.github/with trusted default-branchpushandworkflow_runconsumers. The shared workflows already declareworkflow_call; backport callers must adopt the new contract.pull_requestsignal usestypes: [labeled]to preserve labels added after merge throughworkflow_run. Push covers merges; omittingclosedavoids duplicate backport runs. Consumers neither download artifacts nor check out fork code.origin/maininto the branch, retain the GHCR tag guard from feat(registry): refuse existing ghcr version tags before publication #346, and update workflow docs and regression tests.Test plan
actionlint .github/workflows/*.yaml .github/workflows/*.ymluv run --with zizmor==1.30.1 -- make lintand offline zizmor, using the version pinned in CI.make generate-docsandmake check-docs.Live branch, fork, conflicted PR, merged backport and Linear acceptance remain pending caller rollout. Local tests and CI do not prove production behavior.
Rollout
Merge this shared change first, then publish
resolve-pr-event/v1and the newbackport/v2workflow from the merged commit. Keepbackport/v1unchanged. Direct PR-event backport callers no longer meet the contract. The backport workflow requires read access to PRs and workflow runs.Move Claude review callers to
pull_request. During the transition, the reusable review job accepts legacypull_request_targetcallers only for same-repository PRs. Forks remain excluded, and this PR adds no legacy trigger. A separate inventory tracks caller migration.Caller rollout is tracked in loft-sh/vcluster#4264 and loft-sh/vcluster-docs#3075. Both must use the new contract before rollout. Policy exceptions, merging, release-tag moves and production activation are outside this PR's scope.
Closes DEVOPS-1803